/* * Copyright 2012 Kantega AS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kantega.revoc.agent; import org.kantega.revoc.logging.LogFactory; import org.kantega.revoc.logging.Logger; import org.kantega.revoc.web.JettyStarter; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * */ public class RevocAgent { private static final Logger log = LogFactory.getLogger(RevocAgent.class); /** * Run when Revoc is added as a Java Agent as a startup command line option */ public static void premain(String options, Instrumentation instrumentation) throws Exception { String[] packages = init(options); instrumentation.addTransformer(new RevocClassTransformer(packages)); } /** * Run when Revoc is injected as an agent to an already running JVM */ public static void agentmain(String options, Instrumentation instrumentation) throws Exception { String[] packages = init(options); retransform(instrumentation, packages); } private static String[] init(String options) throws Exception { log.info("Configuring coverage engine"); Properties props = readOptions(options); validatePackagesConfigured(props); String[] packages = getPackagesToInstrument(props); startJettyServer(props, packages); return packages; } private static void retransform(Instrumentation instrumentation, String[] packages) throws UnmodifiableClassException { List<Class> classesToInstrument = new ArrayList<Class>(); for (Class clazz : instrumentation.getAllLoadedClasses()) { if (RevocClassTransformer.shouldFilter(clazz.getClassLoader(), clazz.getName().replace('.', '/'), packages)) { classesToInstrument.add(clazz); } } log.info(String.format("Instrumenting %s classes", classesToInstrument.size())); instrumentation.addTransformer(new RevocClassTransformer(packages), true); instrumentation.retransformClasses(classesToInstrument.toArray(new Class[classesToInstrument.size()])); } private static String[] getPackagesToInstrument(Properties props) { String[] packages = parseList(props.getProperty("packages")); for (int i = 0; i < packages.length; i++) { packages[i] = packages[i].replace('.', '/'); } return packages; } private static void validatePackagesConfigured(Properties props) { String packages = props.getProperty("packages"); if (packages == null) { log.error("Option 'packages' must be specified. Example command line:"); log.error("\tjava -javaagent:revoc.jar=packages=com.example.,port=7070 -jar my.jar:"); System.exit(-1); } else { log.info("Using packages pattern(s) " + packages); } } private static String[] parseList(String list) { return list == null ? null : list.split("\\|"); } private static void startJettyServer(Properties props, String[] packages) throws Exception { new JettyStarter().start(getPort(props.getProperty("port")), packages); } private static int getPort(String port) { if (port != null) { log.info("Using HTTP port " + port); } else { port = "7070"; } try { return Integer.parseInt(port); } catch (NumberFormatException e) { log.error("Port is not a number: " + port); System.exit(-1); return -1; } } private static Properties readOptions(String options) { Properties props = new Properties(); if (options == null) { return props; } for (String abs : options.split(",")) { String[] ab = abs.split("="); props.setProperty(ab[0], ab[1]); } return props; } }